import Context from './context.js';
import _ from 'lodash';

Context(({ funGetSymbols, utils, createSocket, funGetPositions, openLong, openShort, config, getTrades, getBalance }) => {
  const notice = new utils.Notice({
    token: '5bd3ea66-ece5-4a87-b3b1-2a47bb3a91cb'
  });
  // 记录每个交易对的K线收盘数据，key为symbol，value为{[klineEndTime]: klineData}
  const CLOSED_KLINES = {};
  // 记录每个币种上次平仓时间
  const lastTradeTime = {};
  // 预测结果
  const SYMBOL_PREDICT = {};

  // 判断是否满足建仓条件
  async function predictSymbol(symbol, klines) {
    // 标记币种已经加载
    loadedSymbols[symbol] = true;

    const prevKline = klines[1];

    // 如果前一根K线不达标
    if (prevKline.rate < config.gainTriggerPct) {
      return false;
    }

    // 判断是否在持仓中
    const positions = funGetPositions();
    // 判断当前币种是否在持仓中
    if (positions.some(p => p.symbol === symbol)) {
        return false;
    }
    // 判断是否在冷静期
    if (lastTradeTime[symbol]) {
        const diff = (new Date().getTime() - lastTradeTime[symbol]) / 60000;
        if (diff < config.cooldownMinutes) {
            console.log([ '冷静期', symbol, diff ]);
            return false;
        }
    }
    await utils.predict(symbol, '3m', 120, prevKline.time, async (info) => {
      let side = info?.action || '';
      side = side.toUpperCase();
      if (info) {
        SYMBOL_PREDICT[symbol] = {side, time: prevKline.time, ...info, kline: prevKline};
        console.log(`ai 预测结果: ${symbol} ${side} -` + prevKline.time, info);
      }
    });
  }

  // 判断是否开单
  async function canOpenPosition(symbol, klines) {
    // 不存在预测
    const predict = SYMBOL_PREDICT[symbol];
    if (!predict) {
      return false;
    }
    // 判断是否在持仓中
    const positions = funGetPositions();
    // 判断当前币种是否在持仓中
    if (positions.some(p => p.symbol === symbol)) {
        return false;
    }
    let openResult = null;
    if (predict.side === 'SHORT') {
      const kline0 = klines[0];
      const kline1 = klines[1];
      const kline2 = klines[2];
      const kline3 = klines[3];
      // 3 连阴 || 当前价格小于预测时候的价格
      if ((kline1.rate < 0 && kline2.rate < 0 && kline3.rate <0)
            && (kline0.close > predict.kline.close)) {
        // 清除预测
        SYMBOL_PREDICT[symbol] = null;
        let takeProfitPct = predict?.take_profit_percent || config.takeProfitPct;
        takeProfitPct = config.takeProfitPct;
        let stopLossPct = predict?.stop_loss_percent ? (predict?.stop_loss_percent * 2.5) : config.stopLossPct;
        const quantity = getBalance() / kline0.close;
        openResult = await openShort(symbol, quantity, takeProfitPct, stopLossPct);
        notice.bot.sendMsg(`[${config.name}]: ${symbol} ${side} ${predict.kline.time} [${stopLossPct}, ${takeProfitPct}]`);
      }
    }
    // 添加冷静期
    if (openResult) {
      lastTradeTime[symbol] = new Date().getTime();
    }

  }

  // K线数据处理函数
  let onKline = async (key, data, callback, min = 2, max = 5) => {
    const { symbol, k } = data;
    const now = Number(k.t);
    // 初始化该symbol的K线收盘数据
    let closedKlines = CLOSED_KLINES[key] || {};
    closedKlines[symbol]= closedKlines[symbol] || {};

    // 记录本根K线的收盘数据，key为K线结束时间
    closedKlines[symbol][k.t] = {
      open: data.open,
      close: data.close,
      high: data.high,
      time: data.time,
      rate: data.rate,
      closeToHigh: data.closeToHigh,
    };
    // 更新全局变量
    CLOSED_KLINES[key] = closedKlines;

    // 按时间倒序排序
    const times = Object.keys(closedKlines[symbol]).map(Number).sort((a, b) => b - a);

    // 没有持仓时，尝试建仓（必须有上一根K线的收盘数据）
    if (times.length >= min) {
        await callback(symbol, times.map(i => closedKlines[symbol][i]));
    }
    // 只保留最近5根K线，防止内存泄漏（从后往前删）
    if (times.length > max) {
        const removeCount = times.length - max;
        for (let i = 0; i < removeCount; i++) {
            delete closedKlines[symbol][times[times.length - 1 - i]];
        }
    }
  };
  // 记录已加载的币种
  let loadedSymbols = {};
  let loadedSymbolsTimer = null;
  let socket1m = createSocket();
  let socket3m = createSocket();
  let loadSymbolsTimes = 0;
  let loadSymbols = async () => {
    loadSymbolsTimes++;
    if (loadSymbolsTimes < 2) {
        console.log('socket not ready');
        return;
    }
    console.log('loadSymbols ' + new Date().toISOString());
    const symbols = await funGetSymbols();
    // 找出未加载的币种
    const ts = symbols.filter(i => !loadedSymbols[i.symbol]);
    console.log(`loadSymbols: new ${ts.length} symbols`);
    (_.chunk(ts.map(i => `${i.symbol.toLowerCase()}_perpetual@continuousKline_3m`), 200))
            .forEach((chunk, i) => {
                // 发送订阅 continuousKline_3m 数据的请求
                socket3m.send(JSON.stringify({
                    method: 'SUBSCRIBE',
                    params: chunk,
                    id: _.now() + i,
                }));
            });
    // 订阅1m的K线数据
    (_.chunk(ts.map(i => `${i.symbol.toLowerCase()}_perpetual@continuousKline_1m`), 200))
            .forEach((chunk, i) => {
                // 发送订阅 continuousKline_3m 数据的请求
                socket1m.send(JSON.stringify({
                    method: 'SUBSCRIBE',
                    params: chunk,
                    id: _.now() + i + 1000,
                }));
            });
    if (loadedSymbolsTimer) {
        clearTimeout(loadedSymbolsTimer);
    }
    // 1小时后重新加载
    loadedSymbolsTimer = setTimeout(loadSymbols, 1000 * 60 * 60);
  }
  // WebSocket 连接打开时的回调
  socket3m.on('open', async () => {
    await loadSymbols();
  });
  socket1m.on('open', async () => {
    await loadSymbols();
  });
  // WebSocket 收到消息时的回调
  socket3m.on('message', async function (data) {
    try {
      const info = JSON.parse(data);
      // 判断是否为 continuous_kline 类型的数据流
      if (info.stream && info.data.e === 'continuous_kline') {
        if (info.data.k.i !== '3m') {
            return;
        }
        await onKline('3m', utils.formatKline(info), predictSymbol);
      } else {
        console.log(info);
      }
    } catch (error) {
      // 解析消息出错，打印错误信息
      console.error(new Date().toISOString(), 'Error parsing message:', error);
    }
  });
  socket1m.on('message', async function (data) {
      try {
      const info = JSON.parse(data);
      // 判断是否为 continuous_kline 类型的数据流
      if (info.stream && info.data.e === 'continuous_kline') {
        if (info.data.k.i !== '1m') {
            return;
        }
        await onKline('1m', utils.formatKline(info), canOpenPosition, 4);
      } else {
        console.log(info);
      }
    } catch (error) {
      // 解析消息出错，打印错误信息
      console.error(new Date().toISOString(), 'Error parsing message:', error);
    }

  });
});
